#include "gdt.h"
#include "../inc/types.h"
#include "../mm/mmu.h"
#include "../inc/x86.h"

/*cpu权限 ring0和ring3*/
#define DPL_KERNEL    (0)
#define DPL_USER    (3)

#define USER_DS         ((((4) << 3)) | (3))
#define KERNEL_DS       ((((2) << 3)) | (0))
#define KERNEL_CS       ((((1) << 3)) | (0))
#define GD_TSS          ((5) << 3)        // task segment selector

static struct taskstate ts = {0};

static struct segdesc gdt[] = {
    SEG_NULL,
    [1] = SEG(0x8 | 0x2, 0x0, 0xFFFFFFFF, DPL_KERNEL),
    [2] = SEG(0x2, 0x0, 0xFFFFFFFF, DPL_KERNEL),
    [3] = SEG(0x8 | 0x2, 0x0, 0xFFFFFFFF, DPL_USER),
    [4] = SEG(0x2, 0x0, 0xFFFFFFFF, DPL_USER),
    [5]    = SEG_NULL,
};
static struct pseudodesc gdt_pd = {
    sizeof(gdt) - 1, (uint32_t)gdt
};

/* *
 * lgdt - load the global descriptor table register and reset the
 * data/code segement registers for kernel.
 * */
static inline void lgdt(struct pseudodesc *pd) {
    asm volatile ("lgdt (%0)" :: "r" (pd));
    asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS));
    asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS));
    asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS));
    asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS));
    asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS));
    // reload cs
    asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS));
}

/* temporary kernel stack */
uint8_t stack0[1024];
/* gdt_init - initialize the default GDT and TSS */
void gdt_init(void) {
    // Setup a TSS so that we can get the right stack when we trap from
    // user to the kernel. But not safe here, it's only a temporary value,
    // it will be set to KSTACKTOP in lab2.
    ts.ts_esp0 = (uint32_t)&stack0 + sizeof(stack0);
    ts.ts_ss0 = ((((2) << 3)) | (0));

    // initialize the TSS filed of the gdt
    gdt[5] = SEG16(0x9, (uint32_t)&ts, sizeof(ts), DPL_KERNEL);
    gdt[5].sd_s = 0;

    // reload all segment registers
    lgdt(&gdt_pd);

    // load the TSS
    ltr(GD_TSS);
}
